import {
  _decorator,
  Component,
  Node,
  Font,
  BitmapFont,
  CanvasPool,
  Vec2,
  ImageAsset,
  Texture2D,
  Asset,
  RenderTexture,
  MeshRenderer,
  Material,
  Vec3,
  Size,
  utils,
  geometry,
  EventHandler,
  Color,
  Label,
  Camera,
  primitives,
  ccenum,
  Button,
  EventHandler as ComponentEventHandler,
  NodeEventType,
  EventTouch,
  Input,
  input,
  BoxCollider,
  macro
} from "cc";
const { ccclass, property, executeInEditMode, type, menu } = _decorator;
import {
  renderTooltip,
  getTooltipSizeInfo,
  TailPosition,
  TooltipOptions,
  TooltipSizeInfo,
} from "./TooltipRender";

export const BASELINE_RATIO = 0.26;
export const defaultMaterail: Material = new Material();

const MAX_SIZE = 2048;

ccenum(TailPosition);

defaultMaterail.initialize({
  effectName: "builtin-standard",
  technique: 1,
  defines: {
    USE_ALBEDO_MAP: true,
  },
});

@ccclass("Tooltip")
@executeInEditMode
export class Tooltip extends Component {
  private options: TooltipOptions = {
    contentLeft: 0,
    contentTop: 0,
    contentWidth: 100,
    contentHeight: 100,
    contentMargin: 20,
    contentPadding: 10,
    borderRadius: 4,
    primaryColor: "#00FF00",
    backgroundColor: undefined,
    borderWidth: 2,
    borderLength: 40,
    tailLine1Rotate: 30,
    tailLine1Length: 100,
    tailLine2Length: 200,
    tailPostion: TailPosition.BottomRight,
  };

  @type([ComponentEventHandler])
  @property
  public clickEvents: ComponentEventHandler[] = [];

  @property
  @type(TailPosition)
  private _tailPosition: TailPosition = TailPosition.BottomRight;

  @property
  @type(TailPosition)
  public get tailPosition() {
    return this._tailPosition;
  }

  @property
  public followCamera: boolean = true;

  public set tailPosition(val: TailPosition) {
    this._tailPosition = val;
    this.updateRenderData();
  }

  /**
   * 显示文字
   */
  @property
  private _string: string = "Label3D";
  @property({ displayOrder: 2, multiline: true })
  get string() {
    return this._string;
  }
  set string(val) {
    if (val === null || val === undefined) {
      val = "";
    } else {
      val = val.toString();
    }
    if (this._string === val) {
      return;
    }
    this._string = val;
    this.updateRenderData();
  }

  @property
  private _material: Material = null!;
  @property({ type: Material, displayOrder: 2 })
  public get material() {
    return this._material;
  }
  public set material(val) {
    this._material = val;
    this.updateMeshRenderMaterial();
  }

  @property
  private _padding: number = 4;

  @property
  public get padding() {
    return this._padding;
  }

  public set padding(val: number) {
    this._padding = val;
    this.updateRenderData();
  }

  @property
  private _tailLine1Rotate: number = 30;

  @property
  public get tailLine1Rotate() {
    return this._tailLine1Rotate;
  }

  public set tailLine1Rotate(val) {
    this._tailLine1Rotate = val;
    this.updateRenderData();
  }

  @property
  private _tailLine1Length: number = 50;

  @property
  public get tailLine1Length() {
    return this._tailLine1Length;
  }

  public set tailLine1Length(val) {
    this._tailLine1Length = val;
    this.updateRenderData();
  }

  @property
  private _tailLine2Length: number = 100;

  @property
  public get tailLine2Length() {
    return this._tailLine2Length;
  }

  public set tailLine2Length(val) {
    this._tailLine2Length = val;
    this.updateRenderData();
  }

  @property
  private _margin: number = 10;

  @property
  public get margin() {
    return this._margin;
  }

  public set margin(val: number) {
    this._margin = val;
    this.updateRenderData();
  }

  @property
  private _borderWidth: number = 2;

  @property
  public get borderWidth() {
    return this._borderWidth;
  }

  public set borderWidth(val: number) {
    this._borderWidth = val;
    this.updateRenderData();
  }

  @property
  private _borderRadius: number = 4;

  @property
  public get borderRadius() {
    return this._borderRadius;
  }

  public set borderRadius(val: number) {
    this._borderRadius = val;
    this.updateRenderData();
  }

  
  @property
  private _color: Color = new Color(0, 255, 0);

  @property
  public get color() {
    return this._color;
  }

  @property({ type: Camera, serializable: true })
  public camera: Camera;

  private _defaultCamera?: Camera;

  public set color(val: Color) {
    this._color = val;
    this.updateRenderData();
  }

  private _splitStrings: string[] = [];
  private _assemblerData: any = null!;
  private _context: CanvasRenderingContext2D = null!;
  private _canvas: HTMLCanvasElement = null!;
  private _texture: Texture2D = null!;
  private _meshRender: MeshRenderer = null!;
  private _canvasSize: Size = new Size();

  /**
   * mesh uvs
   */
  private _uvs: number[] = [];
  /**
   * mesh 顶点坐标
   */
  private _positions: number[] = [];

  private _startPosition: Vec2 = new Vec2();

  onLoad() {
    this._defaultCamera = this.getDefaultCamera();

    // input.on(Input.EventType.TOUCH_START, () => {}, this);
    this.initMeshRender();
  }

  private getDefaultCamera(): Camera | undefined {
    let root = this.node;
    while (root.parent) {
      root = root.parent;
    }
    for (const node of root.children) {
      const camera = node.getComponent(Camera);
      if (camera) {
        return camera;
      }
    }
  }
  private boxCollider: BoxCollider;

  onEnable() {
    this._assemblerData = CanvasPool.getInstance().get();
    this.updateRenderData();

    // 添加BoxCollider组件
    this.boxCollider = this.node.getComponent(BoxCollider);
    if (this.boxCollider == null) {
      this.boxCollider = this.node.addComponent(BoxCollider);
    }

    this.updateBoxCollider();
  }

  start() {
    this.updateRenderData();
  }

  private updateBoxCollider() {
    if (this.boxCollider == null) {
      return;
    }
    const sizeX =
      (this._renderSizeInfo.contentWidth / this._renderSizeInfo.canvasWidth) *
      (this.geometry.maxPos.x - this.geometry.minPos.x);
    const sizeY =
      (this._renderSizeInfo.contentHeight / this._renderSizeInfo.canvasHeight) *
      (this.geometry.maxPos.y - this.geometry.minPos.y);
    this.boxCollider.size = new Vec3(sizeX, sizeY, 0.001);
    switch (this._tailPosition) {
      case TailPosition.BottomMiddle:
        this.boxCollider.center = new Vec3(0, 1 - sizeY / 2, 0);
        break;
      case TailPosition.BottomLeft:
        this.boxCollider.center = new Vec3( this.geometry.maxPos.x  - sizeX / 2, 1 - sizeY / 2, 0);
        break;
      case TailPosition.BottomRight:
        this.boxCollider.center = new Vec3( this.geometry.minPos.x  + sizeX / 2, 1 - sizeY / 2, 0);
        break;
      case TailPosition.None:
      default:
        this.boxCollider.center = new Vec3(0, 0, 0);
        break;
    }
  }
  /**
   * 刷新渲染
   */
  private updateRenderData(): void {
    if (!this._assemblerData) return;
    this._context = this._assemblerData.context;
    this._canvas = this._assemblerData.canvas;

    this.initTexture2D();
    this.updateFontFormatting();
    this.updateFontCanvasSize();
    this.updateRenderMesh();
    this.updateFontRenderingStyle();
    this.updateTexture();
    this.updateMaterial();
    this.resetRenderData();
    this.updateBoxCollider();
  }
  /**
   * 初始化渲染组件
   */
  private initMeshRender(): void {
    this._meshRender = this.node.getComponent(MeshRenderer)!;
    if (!this._meshRender) {
      this._meshRender = this.node.addComponent(MeshRenderer);
    }
    this.initRenderMesh();
  }
  /**
   * 初始话mesh的基础数据
   * 最少的面数 6个顶点
   */
  private initRenderMesh(): void {
    this._positions.push(-0.5, -0.5, 0);
    this._uvs.push(0, 1);
    this._positions.push(0.5, -0.5, 0);
    this._uvs.push(1, 1);
    this._positions.push(-0.5, 0.5, 0);
    this._uvs.push(0, 0);
    this._positions.push(-0.5, 0.5, 0);
    this._uvs.push(0, 0);
    this._positions.push(0.5, -0.5, 0);
    this._uvs.push(1, 1);
    this._positions.push(0.5, 0.5, 0);
    this._uvs.push(1, 0);

    this._meshRender.mesh = utils.MeshUtils.createMesh({
      positions: this._positions,
      uvs: this._uvs,
      minPos: { x: -0.5, y: -0.5, z: 0 },
      maxPos: { x: 0.5, y: 0.5, z: 0 },
      normals: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],
    });
    this._meshRender.model?.updateWorldBound();
    // this._worldBounds = this._meshRender.model?.worldBounds!;
    // let a = new Vec3();
    // let b = new Vec3();
    // this._worldBounds.getBoundary(a, b);
    // console.log(a, b);
    this.updateMeshRenderMaterial();
  }
  private updateMeshRenderMaterial(): void {
    if (!this._meshRender) return;
    if (this._material) {
      this._meshRender.material = this._material;
    } else {
      this._meshRender.material = defaultMaterail;
    }
  }

  private initTexture2D(): void {
    if (!this._texture) {
      let image: ImageAsset = new ImageAsset(this._canvas);
      this._texture = new Texture2D();
      this._texture.image = image;
    }
  }

  private updateTexture(): void {
    if (!this._context || !this._canvas) return;
    this._context.clearRect(0, 0, this._canvas.width, this._canvas.height);
    let textPosX: number = 0;
    let textPosY: number = 0;

    this._context.fillStyle = `rgba(${this._color.r}, ${this._color.g}, ${this._color.b}, ${this._color.a})`;
    this.renderTooltip(this._context);
    this._context.fillStyle = this.color.toCSS("#rrggbb");
    const { contentLeft, contentWidth } = this._renderSizeInfo;

    const lineHeight = this.getLineHeight();
    for (let i = 0; i < this._splitStrings.length; i++) {
      textPosY = this._startPosition.y + (i + 1) * lineHeight;
      const { width: textWidth } = this._context.measureText(
        this._splitStrings[i]
      );
      textPosX = contentLeft + (contentWidth - textWidth) / 2;

      this._context.fillText(
        this._splitStrings[i],
        textPosX,
        textPosY + this._padding + this._margin
      );
    }
    let uploadAgain: boolean =
      this._canvas.width !== 0 && this._canvas.height !== 0;
    if (uploadAgain) {
      this._texture.reset({
        width: this._canvas.width,
        height: this._canvas.height,
        mipmapLevel: 1,
      });
      this._texture.uploadData(this._canvas);
      this._texture.setWrapMode(
        RenderTexture.WrapMode.CLAMP_TO_EDGE,
        RenderTexture.WrapMode.CLAMP_TO_EDGE
      );
    }
  }

  private updateMaterial(): void {
    if (!this._texture) return;
    if (!this._meshRender) return;
    // if (!this._material) return;
    let material: Material = this._meshRender.getMaterialInstance(0)!;
    material.setProperty("mainTexture", this._texture);
  }
  /**
   * 字体格式化
   * “\n” 为换行符号
   */
  private updateFontFormatting(): void {
    if (!this._context) return;
    let strs: string[] = this._string.split("\n");
    this._splitStrings = strs;
    for (let i = 0; i < strs.length; i++) {
      //获取文本的宽度
      let len: number = this._context.measureText(strs[i]).width;
      if (len > this._canvasSize.width) {
        this._canvasSize.width = len;
      }
    }
    this._canvasSize.height =
      strs.length * this.getLineHeight() +
      BASELINE_RATIO * this.getLineHeight();
  }

  private _renderSizeInfo: TooltipSizeInfo;
  /**
   * 根据字体的样式
   * 更新canvas的size
   */
  private updateFontCanvasSize(): void {
    const opts = this.getTooltipOptions(
      0,
      0,
      this._canvasSize.width,
      this._canvasSize.height
    );
    this._renderSizeInfo = getTooltipSizeInfo(opts);
    this._canvasSize.width = Math.min(
      this._renderSizeInfo.canvasWidth,
      MAX_SIZE
    );
    this._canvasSize.height = Math.min(
      this._renderSizeInfo.canvasHeight,
      MAX_SIZE
    );

    if (this._canvas.width != this._canvasSize.width) {
      this._canvas.width = this._canvasSize.width;
    }
    if (this._canvas.height != this._canvasSize.height) {
      this._canvas.height = this._canvasSize.height;
    }
    this._context.font = this.getFontDesc();
  }

  private getTooltipOptions(
    contentTop: number,
    contentLeft: number,
    contentWidth: number,
    contentHeight: number
  ): TooltipOptions {
    const opts = this.options;
    if (contentWidth) {
      opts.contentWidth = contentWidth;
    }
    if (contentHeight) {
      opts.contentHeight = contentHeight;
    }
    if (contentTop != undefined) {
      opts.contentTop = contentTop;
    }
    if (contentLeft != undefined) {
      opts.contentLeft = contentLeft;
    }
    opts.tailPostion = this._tailPosition;
    opts.contentPadding = this._padding;
    opts.contentMargin = this._margin;
    opts.primaryColor = this._color.toCSS("#rrggbb");
    opts.tailLine1Rotate = this._tailLine1Rotate;
    opts.tailLine1Length = this._tailLine1Length;
    opts.tailLine2Length = this._tailLine2Length;
    opts.borderWidth = this._borderWidth;
    opts.borderRadius = this._borderRadius;
    return opts;
  }

  private renderTooltip(ctx) {
    if (!this._renderSizeInfo) {
      return;
    }

    const { contentLeft, contentTop, contentWidth, contentHeight } =
      this._renderSizeInfo;

    const opts = this.getTooltipOptions(
      contentTop,
      contentLeft,
      contentWidth,
      contentHeight
    );

    renderTooltip(ctx, opts);
  }

  private updateFontRenderingStyle(): void {
    this._context.font = this.getFontDesc();
    this._context.lineJoin = "round";
    this._context.textAlign = "left";
    this._context.textBaseline = "alphabetic";
    this._context.fillStyle = `rgba(${this.color.r}, ${this.color.g}, ${this.color.b}, ${this.color.a})`;
  }
  private geometry: primitives.IGeometry;
  /**
   * 根据canvas的实际宽高
   * 动态的调整mesh的坐标
   */
  private updateRenderMesh(): void {
    let rate: number = this._canvas.width / this._canvas.height;
    this._positions = [];
    this._positions.push(-0.5 * rate, -0.5, 0);
    this._positions.push(0.5 * rate, -0.5, 0);
    this._positions.push(-0.5 * rate, 0.5, 0);
    this._positions.push(-0.5 * rate, 0.5, 0);
    this._positions.push(0.5 * rate, -0.5, 0);
    this._positions.push(0.5 * rate, 0.5, 0);
    const geometry: primitives.IGeometry = {
      positions: this._positions,
      uvs: this._uvs,
      minPos: { x: -0.5 * rate, y: -0.5, z: 0 },
      maxPos: { x: 0.5 * rate, y: 0.5, z: 0 },
      normals: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],
    };
    this.resetOriginalLocation(geometry);
    this._meshRender.mesh = utils.MeshUtils.createMesh(geometry);
    this._meshRender.model?.updateWorldBound();
    this.geometry = geometry;

    this.updateMeshRenderMaterial();
  }

  private resetOriginalLocation(geometry: primitives.IGeometry) {
    let dx;
    let dy;

    switch (this._tailPosition) {
      case TailPosition.BottomLeft:
        dx = geometry.minPos.x;
        dy = geometry.minPos.y;
        break;
      case TailPosition.BottomRight:
        dx = geometry.maxPos.x;
        dy = geometry.minPos.y;
        break;
      case TailPosition.BottomMiddle:
        dx = 0;
        dy = geometry.minPos.y;
        break;
      case TailPosition.None:
      default:
        dx = 0;
        dy = 0;
        break;
    }
    for (let index = 0; index < geometry.positions.length; index += 3) {
      geometry.positions[index] -= dx;
      geometry.positions[index + 1] -= dy;
    }
    geometry.minPos.x -= dx;
    geometry.minPos.y -= dy;

    geometry.maxPos.x -= dx;
    geometry.maxPos.y -= dy;
  }
  /**
   * 获取行高
   */
  private getLineHeight(): number {
    return 52; //行高 -暂时写成40
  }

  private getFontDesc() {
    let fontDesc: string = "";
    //字体大小
    fontDesc += "50px ";
    fontDesc += "Arial";
    return fontDesc;
  }

  private resetRenderData(): void {
    this._canvasSize.width = 0;
    this._canvasSize.height = 0;
  }

  update(deltaTime: number) {
    if (this.followCamera) {
      if (this.camera) {
        this.node.setWorldRotation(this.camera.node.getWorldRotation());
      } else if (this._defaultCamera) {
        this.node.setWorldRotation(this._defaultCamera.node.getWorldRotation());
      }
    }
  }

  onDisable() {
    if (this._assemblerData) {
      CanvasPool.getInstance().put(this._assemblerData);
    }
    this._meshRender = null!;
  }
}
